package j.util.jdbc.simple.core;

import j.util.New;
import j.util.StringUtils;
import j.util.jdbc.simple.core.support.SimpleJdbcUtils;
import j.util.jdbc.simple.core.support.SqlUtils;
import j.util.jdbc.simple.metadata.Column;
import j.util.jdbc.simple.metadata.InsertAttribute;
import j.util.jdbc.simple.metadata.TableMetaData;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 对数据库表的插入操作的sql语句
 * 
 * @author 刘军海
 * @version Sep 8, 2011 9:41:00 AM
 * 
 */
public class SQLForInsert {

	private TableMetaData tmd;
	private InsertAttribute innsertAttribute;

	public SQLForInsert(TableMetaData tmd) {
		this.tmd = tmd;
		innsertAttribute = tmd.getInsertAttribute();
	}

	private String _checkedSql;
	private List<String> checkedOrder;

	/**
	 * 获取insert语句前的检查语句，该检查根据该表所设置的pk uk进行构造where语句部分，并且仅生成一次
	 * @return
	 */
	public String getCheckedSql() {
		if(!innsertAttribute.isChecked()){
			//不需要执行插入前检测
			return "";
		}
		if (StringUtils.isNullOrEmpty(_checkedSql)) {
			synchronized (this) {
				if (StringUtils.isNullOrEmpty(_checkedSql)) {
					// 在这里调用仅生成一次
					_createCheckedSql();
				}
			}
		}
		return _checkedSql;
	}

	/**
	 * 构造插入前检查sql语句，复合键类的要求用逗号隔开
	 * 若有复合键，复合键间之间以and连接，整个复合键外围加扩号
	 * 构造的同时将会自动生成checkedOrder参数顺序
	 */
	private void _createCheckedSql() {
		StringBuilder sb = new StringBuilder("SELECT COUNT(1) FROM ").append(
				tmd.getTableName()).append(" ");
		String s = "";
		this.checkedOrder = New.list();
		//先构造uk
		List<String> columns = New.list();
		if(null!=tmd.getUkColumns() && tmd.getUkColumns().size()>0){
            columns.addAll(tmd.getUkColumns());
        }
        if(columns.size()==0){
            //不需要验证
            innsertAttribute.setChecked(false);
            return;
        }
		/*if(StringUtils.isNotEmpty(tmd.getPkColumns()))
			columns.add(tmd.getPkColumns());*/
		s+= SqlUtils.fromColumnsToWhereSqlForDMLCheck(columns,
                this.checkedOrder);
		if(s.length()>0){
			sb.append("WHERE ").append(s);
		}
		_checkedSql = sb.toString();
	}

	public List<String> getCheckedOrder() {
		return checkedOrder;
	}

	/**
	 * 获取insert sql语句，该语句每次调用都会自动生成，并且要求调用方构造一个空的参数顺序，即insertOrder，类型为List<String>
	 * 插入语句构造完成后，将会自动将本次sql语句的参数顺序设置到insertOrder中
	 * @param args
	 * @param insertOrder
	 * @return
	 */
	public String getInsertSql(Map<String, Object> args,
			List<String> insertOrder) {
		StringBuilder sql = new StringBuilder("INSERT INTO ");
		sql.append(tmd.getTableName()).append("(");
		Map<String,Column> columns = tmd.getColumns();
		String cname = "";
		int i = 0;
		Set<String> usedColumns = tmd.getUpdateAttribute().getUsedColumns();
		for(Column c : columns.values()){
            if(!c.isInsertable()){
                continue;
            }
			cname = c.getName();
			if(null != tmd.getGeneratedKeyColumns() && tmd.getGeneratedKeyColumns().contains(cname)){
				continue;
			}
			if(null!=usedColumns && !usedColumns.contains(cname)){
				continue;
			}

			if(null == args.get(cname)){
				//没有为该字段设置值，检查该字段是否为必填项且有默认值，若有默认值，则不必出现在insert into列表中
				if(!c.isNullable() && !c.isHasDefaultValue()){
					//该字段为必填并且没有默认值，抛出异常
					throw new RuntimeException("Table name: "+tmd.getTableName() +"  db column "+cname+" is not nullable,but dont't specify the value.");
				}
			}
			if(i++>0){
				sql.append(",");
			}
			sql.append(cname);
			insertOrder.add(cname);

		}
		if(insertOrder.size() == 0){
			//没有生成一个插入列，直接抛出异常
			throw new RuntimeException("table "+tmd.getTableName()+" no columns to insert.");
		}
		sql.append(")VALUES(");
		sql.append(SimpleJdbcUtils.computeJdbcParameters(insertOrder.size()))
			.append(")");
		return sql.toString();
	}
	/**
	 * 根据指定的列生成sql语句
	 * @param columns
	 * @return
	 */
	public String getInsertSqlBySpecificColumns(Set<String> columns,List<String> columnsOrder){
		if(null == columns || columns.size()==0){
			throw new RuntimeException("don't specify columns for table "+tmd.getTableName());
		}
		StringBuilder sql = new StringBuilder("INSERT INTO ");
		sql.append(tmd.getTableName()).append("(");
		String ps = "";
		int i = 0;
		for(String s : columns){
			if(i++>0){
				sql.append(",");
				ps+=",";
			}
			sql.append(s);
			ps += "?";
			if(null != columnsOrder){
				columnsOrder.add(s.toUpperCase());
			}
		}
		sql.append(")VALUES(").append(ps).append(")");
		return sql.toString();
	}
}
